///////////////////////////////////////////////////////////
//
// File: HaptHIDDevice.cpp
//
// Description:
//
///////////////////////////////////////////////////////////

#include "HaptCtrl.h"      // Required PCH
#include "HaptHIDDevice.h" // Required Win32

HaptHIDDevice::HaptHIDDevice()
: _hDevice(INVALID_HANDLE_VALUE),
  _hEvent(INVALID_HANDLE_VALUE),
  _hidCap(),
  _overlap(),
  _devicePath(""),
  _uniqID(-1)
{
}

HaptHIDDevice::~HaptHIDDevice()
{
}

bool 
HaptHIDDevice::wait( unsigned int time )
{
    WaitForSingleObject(_hEvent, time );
    return true;
}

bool 
HaptHIDDevice::disconnect()
{
	CloseHandle(_hDevice);
    return true;
}

bool 
HaptHIDDevice::queryDeviceCapabilities( HaptHIDDevice &device )
{
	PHIDP_PREPARSED_DATA PreparsedData;

	HidD_GetPreparsedData(device._hDevice, &PreparsedData);
    HidP_GetCaps(PreparsedData, &device._hidCap);
    HidD_FreePreparsedData(PreparsedData);

    // TODO: Output the device capabilities

    return true;
}

bool 
HaptHIDDevice::outputToDevice( HaptHIDDevice &device, byte *pBuff, DWORD &oBytes )
{
    // Set output number of bytes to 0
    oBytes = 0;
    
    // Early out if device handle is invalid
    if ( device._hDevice == INVALID_HANDLE_VALUE )
        return true;
	
    DWORD bytes_written = 0;
	bool res = WriteFile(  device._hDevice, pBuff, 
                           device._hidCap.OutputReportByteLength, 
                          &bytes_written, 
                          &device._overlap );
    SleepEx( 50, true );

    return res;
}

bool 
HaptHIDDevice::connectToDevices( int vendor, int product, vector<HaptHIDDevice> &ovDev  )
{
    HANDLE WriteHandle  = INVALID_HANDLE_VALUE, 
           DeviceHandle = INVALID_HANDLE_VALUE,
	       hDevInfo     = INVALID_HANDLE_VALUE;

	HIDD_ATTRIBUTES						Attributes;
	SP_DEVICE_INTERFACE_DATA			devInfoData;
	int									MemberIndex = 0;
	LONG								Result;	
	GUID								HidGuid;
	PSP_DEVICE_INTERFACE_DETAIL_DATA	detailData;

    DWORD outBytes;
    unsigned char pChar[256];
	ULONG Required = 0;
	ULONG Length = 0;
	detailData = NULL;
	DeviceHandle = NULL;

	HidD_GetHidGuid(&HidGuid);	
	
	hDevInfo = SetupDiGetClassDevs(&HidGuid, NULL, NULL, DIGCF_PRESENT|DIGCF_INTERFACEDEVICE);
		
	devInfoData.cbSize = sizeof(devInfoData);
	MemberIndex = 0;

	do
	{
		// Got any more devices?
		Result = SetupDiEnumDeviceInterfaces (hDevInfo, 0, &HidGuid, MemberIndex, &devInfoData);

		if (Result == 0)
			break;

		// Call once to get the needed buffer length
		Result = SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInfoData, NULL, 0, &Length, NULL);
		detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(Length);
		detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

		// After allocating, call again to get data
		Result = SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInfoData, detailData, Length, 
			&Required, NULL);

		DeviceHandle = CreateFile( detailData->DevicePath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 
			                       NULL, OPEN_EXISTING,	FILE_FLAG_OVERLAPPED, NULL );

		Attributes.Size = sizeof(Attributes);

		Result = HidD_GetAttributes(DeviceHandle, &Attributes);
		
		if (Attributes.VendorID == vendor && Attributes.ProductID == product )
		{
			// If the vendor and product IDs match, we've found a wiimote 
			HaptHIDDevice device;

			device._devicePath = detailData->DevicePath;
			device._hDevice = DeviceHandle;

			queryDeviceCapabilities(device);

            // Get an event object for the overlapped structure.
	        if (device._hEvent == INVALID_HANDLE_VALUE)
	        {
		        string empty("");
		        device._hEvent = CreateEvent (NULL, TRUE, TRUE, empty.c_str());
		        device._overlap.hEvent = device._hEvent;
		        device._overlap.Offset = 0;
		        device._overlap.OffsetHigh = 0;
	        }

			device._uniqID = ovDev.size() + 1;

            memset(pChar,0x00,256);
            device.queryEvent(pChar,outBytes);

            if ( pChar[0] != 0x00 ) {
                ovDev.push_back(device);
            } else {
                device.disconnect();
            }
		}
		else
		{
			CloseHandle(DeviceHandle);
		}

		free(detailData);

		MemberIndex = MemberIndex + 1;
	} 
	while (true);

	SetupDiDestroyDeviceInfoList(hDevInfo);

    return true;
}

DWORD 
HaptHIDDevice::queryEvent( unsigned char aChar[], DWORD &oBytes )
{
	DWORD result;
	
	// Issue a read request
	if (_hDevice != INVALID_HANDLE_VALUE)
	{
		result = ReadFile( _hDevice, aChar, 
                           _hidCap.InputReportByteLength, 
			               &oBytes, (LPOVERLAPPED)&_overlap );																						  
	}
 
	// Wait for read to finish
	result = WaitForSingleObject(_hEvent, 300);

	ResetEvent(_hEvent);

    return result;
}
